/*
 * Copyright (C) 2006-2010 - Frictional Games
 *
 * This file is part of HPL1 Engine.
 *
 * HPL1 Engine is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * HPL1 Engine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with HPL1 Engine.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "scene/TileMap.h"
#include "math/Math.h"
#include "resources/ResourceImage.h"
#include "scene/TileMapRectIt.h"
#include "scene/TileMapLineIt.h"
#include "resources/Resources.h"
#include "system/LowLevelSystem.h"
#include "graphics/Renderer2D.h"
#include "resources/TileSetManager.h"
#include "graphics/GraphicsTypes.h"
#include "graphics/Renderer2D.h"
#include "graphics/RenderObject2D.h"

namespace hpl {

	//////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS
	//////////////////////////////////////////////////////////////////////////

	//-----------------------------------------------------------------------

	cTileMap::cTileMap(cVector2l avSize, float afTileSize,cGraphics* apGraphics, cResources * apResources)
	{
		mfTileSize = 1;
		mvSize = avSize;
		mfTileSize = afTileSize;
		mpGraphics = apGraphics;
		mpResources = apResources;
		mlShadowLayer =0;
	}

	//-----------------------------------------------------------------------

	cTileMap::~cTileMap()
	{
		Log(" Deleting tilemap.\n");
		for(tTileLayerVecIt it=mvTileLayer.begin();it!=mvTileLayer.end();it++)
			hplDelete(*it);
		for(tTileSetVecIt it = mvTileSet.begin();it != mvTileSet.end();it++)
			mpResources->GetTileSetManager()->Destroy(*it);
	}

	//-----------------------------------------------------------------------

	//////////////////////////////////////////////////////////////////////////
	// PUBLIC METHODS
	//////////////////////////////////////////////////////////////////////////

	//-----------------------------------------------------------------------

	void cTileMap::Render(cCamera2D* apCam)
	{
		cRect2f Rect;
		apCam->GetClipRect(Rect);

		iTileMapIt* TileIt = GetRectIterator(Rect,-1);

		while(TileIt->HasNext())
		{
			RenderTileData(TileIt->Next(), TileIt->GetCurrentLayer());
		}

		hplDelete(TileIt);
	}

	//-----------------------------------------------------------------------

	iTileMapIt* cTileMap::GetRectIterator(const cRect2f& aRect, int alLayer)
	{
		cVector2l vPos = cVector2l((int)floor(aRect.x/mfTileSize),
									(int)floor(aRect.y/mfTileSize));

		cVector2l vSize = cVector2l((int)(aRect.w/mfTileSize)+1,
									(int)(aRect.h/mfTileSize)+1);

		//Check if we need yet another grid for x and y
		if(aRect.x+aRect.w>=(vPos.x+vSize.x)*mfTileSize)vSize.x++;
		if(aRect.y+aRect.h>=(vPos.y+vSize.y)*mfTileSize)vSize.y++;

		//Log("\nPos: %d:%d\n",vPos.x,vPos.y);
		//Log("Size: %d:%d\n\n",vSize.x,vSize.y);

		return hplNew( cTileMapRectIt, (vPos,vSize,this,alLayer) );
	}

	//-----------------------------------------------------------------------

	iTileMapIt* cTileMap::GetLineIterator(const cVector2f& avStart,const cVector2f& avEnd , int alLayer)
	{
		return hplNew( cTileMapLineIt, (avStart,avEnd,this,alLayer) );
	}

	//-----------------------------------------------------------------------


	cVector2f cTileMap::GetWorldPos(cVector2f avScreenPos, cCamera2D* apCam)
	{
		cVector2f vWorldPos;
		cRect2f Rect;
		apCam->GetClipRect(Rect);
		cVector2f vVirtSize = mpGraphics->GetLowLevel()->GetVirtualSize();

		vWorldPos.x = Rect.x + Rect.w*(avScreenPos.x/((float)vVirtSize.x));
		vWorldPos.y = Rect.y + Rect.h*(avScreenPos.y/((float)vVirtSize.y));

		return vWorldPos;
	}

	//-----------------------------------------------------------------------

	cTile* cTileMap::GetScreenTile(cVector2f avPos, int alLayer, cCamera2D* apCam)
	{
		return GetWorldTile(GetWorldPos(avPos,apCam), alLayer);
	}

	//-----------------------------------------------------------------------

	void cTileMap::SetScreenTileData(cVector2f avPos, int alLayer,cCamera2D* apCam,
									int alTileSet, int alTileNum)
	{
		cVector2f vWorldPos = GetWorldPos(avPos,apCam);

		cTile* pOldTile = GetWorldTile(vWorldPos,alLayer);

		iTileData* pData=NULL;
		if(alTileSet>=0)
		{
			cTileSet *pSet = GetTileSet(alTileSet);
			if(pSet==NULL)return;
			pData = pSet->Get(alTileNum);
			if(pData==NULL)return;
		}

		cVector2l vTilePos = cVector2l((int)floor(vWorldPos.x/mfTileSize), (int)floor(vWorldPos.y/mfTileSize));
		if(vTilePos.x<0 || vTilePos.y<0 || vTilePos.x>=mvSize.x || vTilePos.y>=mvSize.y)return;

		if(alLayer<0 || alLayer>=(int)mvTileLayer.size())return;

		cVector3f vTileWorldPos(vTilePos.x*mfTileSize, vTilePos.y*mfTileSize, mvTileLayer[alLayer]->GetZ());

		int lAngle = 0;
		if(pOldTile)lAngle = pOldTile->GetAngle();

		cTile* pTile=NULL;
		if(pData)
			pTile = hplNew( cTile, (pData,(eTileRotation)lAngle,vTileWorldPos,mfTileSize,NULL) );

		mvTileLayer[alLayer]->SetTile(vTilePos.x, vTilePos.y,pTile);
	}

	//-----------------------------------------------------------------------

	void cTileMap::SetScreenTileAngle(cVector2f avPos, int alLayer,cCamera2D* apCam, int alAngle)
	{
		cVector2f vWorldPos = GetWorldPos(avPos,apCam);
		cTile* pTile = GetWorldTile(vWorldPos,alLayer);
		if(pTile==NULL)return;

		cVector2l vTilePos = cVector2l((int)floor(vWorldPos.x/mfTileSize), (int)floor(vWorldPos.y/mfTileSize));
		if(vTilePos.x<0 || vTilePos.y<0 || vTilePos.x>=mvSize.x || vTilePos.y>=mvSize.y)return;

		cVector3f vTileWorldPos(vTilePos.x*mfTileSize, vTilePos.y*mfTileSize, mvTileLayer[alLayer]->GetZ());

		pTile->SetAngle((eTileRotation)alAngle);
	}

	//-----------------------------------------------------------------------

	cTile* cTileMap::GetWorldTile(cVector2f avPos, int alLayer)
	{
		if(alLayer<0 || alLayer>=(int)mvTileLayer.size())return NULL;

		return mvTileLayer[alLayer]->GetAt((int)floor(avPos.x/mfTileSize),
											(int)floor(avPos.y/mfTileSize));
	}

	//-----------------------------------------------------------------------

	/**
	 *
	 * \param alTileNum
	 * \param alLayer
	 * \param avDir 0=left, 1=right, 2=up, 3 = down
	 * \return num of neighbours
	 */
	int cTileMap::GetTileNeighbours4Dir(int alTileNum,int alLayer, bool* avDir)
	{
		int lCount=0;
		cTileLayer* pLayer = mvTileLayer[alLayer];
		int lX = alTileNum%mvSize.x;
		int lY = alTileNum/mvSize.x;

		/*avDir[0] = pLayer->GetAt(lX-1,lY)!=NULL?true:false;
		avDir[1] = pLayer->GetAt(lX+1,lY)!=NULL?true:false;
		avDir[2] = pLayer->GetAt(lX,lY-1)!=NULL?true:false;
		avDir[3] = pLayer->GetAt(lX,lY+1)!=NULL?true:false;*/

		cTile *pTile;
		cTileDataNormal *pData;

		cVector2l vAdd[4] = {cVector2l(-1,0),cVector2l(+1,0),cVector2l(0,-1),cVector2l(0,+1)};

		for(int i=0;i<4;i++)
		{
			pTile = pLayer->GetAt(lX+vAdd[i].x,lY + vAdd[i].y);
			if(pTile != NULL){
				pData = static_cast<cTileDataNormal*>(pTile->GetTileData());
				if(pData->IsSolid())avDir[i] = true;
				else avDir[i] = false;
			}
			else {
				avDir[i] = false;
			}
		}

		for(int i=0;i<4;i++)if(avDir[i]==true)lCount++;

		return lCount;
	}

	//-----------------------------------------------------------------------

	void cTileMap::AddTileLayerFront(cTileLayer *apLayer)
	{
		if(mvTileLayer.size()<1)
		{
			mvTileLayer.push_back(apLayer);
		}
		else
		{
			mvTileLayer.push_back(NULL);
			for(int i=(int)mvTileLayer.size()-2;i>=0;i--)
			{
				mvTileLayer[i+1] = mvTileLayer[i];
			}
			mvTileLayer[0] = apLayer;
		}
	}

	//-----------------------------------------------------------------------

	//////////////////////////////////////////////////////////////////////////
	// PRIVATE METHODS
	//////////////////////////////////////////////////////////////////////////

	//-----------------------------------------------------------------------

	void cTileMap::RenderTileData(cTile* apTile, int alLayer)
	{
		static int count=0;

		cTileDataNormal* pData = static_cast<cTileDataNormal*>(apTile->GetTileData());
		if(pData==NULL)return;
		cRect2f _obj = cRect2f(apTile->GetPosition().x-mfTileSize/2,
								apTile->GetPosition().y-mfTileSize/2, mfTileSize,mfTileSize);
		cRenderObject2D _obj2 = cRenderObject2D(pData->GetMaterial(),
								pData->GetVertexVec(apTile->GetAngle()),
								pData->GetIndexVec(apTile->GetAngle()),
								ePrimitiveType_Quad, apTile->GetPosition().z,
								_obj,
								NULL,apTile->GetPositionPtr());
		mpGraphics->GetRenderer2D()->AddObject(_obj2);

		count++;
	}

	//-----------------------------------------------------------------------

}
